package cn.sskxyz.mongodb.compress;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;

import com.mongodb.CursorType;
import com.mongodb.ExplainVerbosity;
import com.mongodb.Function;
import com.mongodb.MongoNamespace;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoIterable;
import com.mongodb.client.model.Collation;

public class CompressFindIterable<TResult> implements FindIterable<TResult> {

	private FindIterable<TResult> original;

	private List<CompressKey> compressKeys;

	private MongoNamespace namespace;

	private CompressProcessor compressProcessor;

	public CompressFindIterable(FindIterable<TResult> original, List<CompressKey> compressKeys,
			MongoNamespace namespace, CompressProcessor compressProcessor) {
		this.original = original;
		this.compressKeys = compressKeys;
		this.namespace = namespace;
		this.compressProcessor = compressProcessor;
	}

	@Override
	public MongoCursor<TResult> iterator() {
		MongoCursor<TResult> delegated = original.iterator();
		return new CompressBatchCursor<>(delegated, namespace, compressKeys, compressProcessor);
	}

	@Override
	public MongoCursor<TResult> cursor() {
		return this.iterator();
	}

	@Override
	public TResult first() {
		TResult first = original.first();
		this.compressProcessor.decompress(first, compressKeys, namespace);
		return first;
	}

	@Override
	public void forEach(Consumer<? super TResult> action) {
		original.forEach(tr -> {
			this.compressProcessor.decompress(tr, compressKeys, namespace);
			action.accept(tr);
		});
	}

	@Override
	public <U> MongoIterable<U> map(Function<TResult, U> mapper) {
		return original.map(mapper);
	}

	@Override
	public <A extends Collection<? super TResult>> A into(A target) {
		return original.into(target);
	}

	@Override
	public FindIterable<TResult> filter(Bson filter) {
		original.filter(filter);
		return this;
	}

	@Override
	public FindIterable<TResult> limit(int limit) {
		original.limit(limit);
		return this;
	}

	@Override
	public FindIterable<TResult> skip(int skip) {
		original.skip(skip);
		return this;
	}

	@Override
	public FindIterable<TResult> maxTime(long maxTime, TimeUnit timeUnit) {
		original.maxTime(maxTime, timeUnit);
		return this;
	}

	@Override
	public FindIterable<TResult> maxAwaitTime(long maxAwaitTime, TimeUnit timeUnit) {
		original.maxAwaitTime(maxAwaitTime, timeUnit);
		return this;
	}

	@Override
	public FindIterable<TResult> projection(Bson projection) {
		original.projection(projection);
		return this;
	}

	@Override
	public FindIterable<TResult> sort(Bson sort) {
		original.sort(sort);
		return this;
	}

	@Override
	public FindIterable<TResult> noCursorTimeout(boolean noCursorTimeout) {
		original.noCursorTimeout(noCursorTimeout);
		return this;
	}

	@Override
	public FindIterable<TResult> oplogReplay(boolean oplogReplay) {
		original.oplogReplay(oplogReplay);
		return this;
	}

	@Override
	public FindIterable<TResult> partial(boolean partial) {
		original.partial(partial);
		return this;
	}

	@Override
	public FindIterable<TResult> cursorType(CursorType cursorType) {
		original.cursorType(cursorType);
		return this;
	}

	@Override
	public FindIterable<TResult> batchSize(int batchSize) {
		original.batchSize(batchSize);
		return this;
	}

	@Override
	public FindIterable<TResult> collation(Collation collation) {
		original.collation(collation);
		return this;
	}

	@Override
	public FindIterable<TResult> comment(String comment) {
		original.comment(comment);
		return this;
	}

	@Override
	public FindIterable<TResult> hint(Bson hint) {
		original.hint(hint);
		return this;
	}

	@Override
	public FindIterable<TResult> hintString(String hint) {
		original.hintString(hint);
		return this;
	}

	@Override
	public FindIterable<TResult> max(Bson max) {
		original.max(max);
		return this;
	}

	@Override
	public FindIterable<TResult> min(Bson min) {
		original.min(min);
		return this;
	}

	@Override
	public FindIterable<TResult> returnKey(boolean returnKey) {
		original.returnKey(returnKey);
		return this;
	}

	@Override
	public FindIterable<TResult> showRecordId(boolean showRecordId) {
		original.showRecordId(showRecordId);
		return this;
	}

	@Override
	public FindIterable<TResult> allowDiskUse(Boolean allowDiskUse) {
		original.allowDiskUse(allowDiskUse);
		return this;
	}

	@Override
	public FindIterable<TResult> comment(BsonValue comment) {
		original.comment(comment);
		return this;
	}

	@Override
	public Document explain() {
		return original.explain();
	}

	@Override
	public Document explain(ExplainVerbosity verbosity) {
		return original.explain(verbosity);
	}

	@Override
	public <E> E explain(Class<E> explainResultClass) {
		return original.explain(explainResultClass);
	}

	@Override
	public <E> E explain(Class<E> explainResultClass, ExplainVerbosity verbosity) {
		return original.explain(explainResultClass, verbosity);
	}

	@Override
	public FindIterable<TResult> let(Bson variables) {
		original.let(variables);
		return this;
	}

}
